package com.ndkj.blog.service.impl;

import com.ndkj.blog.constant.BlogConstant;
import com.ndkj.blog.constant.CacheConstant;
import com.ndkj.blog.constant.MailConstant;
import com.ndkj.blog.mapper.AdminMapper;
import com.ndkj.blog.pojo.entity.Blog;
import com.ndkj.blog.pojo.entity.Comment;
import com.ndkj.blog.pojo.entity.IpLog;
import com.ndkj.blog.pojo.vo.BlogVo;
import com.ndkj.blog.pojo.vo.CommentVo;
import com.ndkj.blog.service.AdminService;
import com.ndkj.blog.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import org.springframework.util.CollectionUtils;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author 93193
 */
@Service
@Slf4j
public class AdminServiceImpl implements AdminService {

    @Autowired
    private AdminMapper adminMapper;
    @Autowired
    private JavaMailSenderImpl javaMailSender;
    @Autowired(required = false)
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private MailUtils mailUtils;
    @Autowired
    private LoginUserCheck loginUserCheck;
    @Autowired
    private BlogServiceImpl blogService;

    @Override
    public Integer isLoginSuccess(String adminName, String adminPwd) {
        return adminMapper.isLoginSuccess(adminName, adminPwd);
    }

    @Override
    public Integer accessNum() {
        return adminMapper.accessNum();
    }

    @Override
    public List<String> queryArgsList() {
        return adminMapper.queryArgsList();
    }

    @Override
    public Integer addBlog(Blog blog) {
        return adminMapper.addBlog(blog);
    }

    @Override
    public String dbVersion() {
        return adminMapper.dbVersion();
    }

    @Override
    public Integer blogsNum() {
        return adminMapper.blogsNum();
    }

    @Override
    public Integer queryArgsNum() {
        return adminMapper.queryArgsNum();
    }

    @Override
    public Integer adminNum() {
        return adminMapper.adminNum();
    }

    /**
     * 返回登录次数
     */
    @Override
    public Integer loginNum(String adminName) {
        return adminMapper.loginNum(adminName);
    }

    @Override
    public Integer addLoginNum(String userName) {
        return adminMapper.addLoginNum(userName);
    }

    @Override
    public Integer updateLoginTime(String adminName, Date loginTime) {
        return adminMapper.updateLoginTime(adminName, loginTime);
    }

    @Override
    public Date queryLoginTimeByName(String adminName) {
        return adminMapper.queryLoginTimeByName(adminName);
    }

    @Override
    public List<Blog> queryAllBlogs() {
        return adminMapper.queryAllBlogs();
    }

    @Override
    public Integer deleteBlogById(Integer blogId) {
        return adminMapper.deleteBlogById(blogId);
    }

    @Override
    public Blog queryBlogById(Integer blogId) {
        return adminMapper.queryBlogById(blogId);
    }

    @Override
    public Integer updateBlog(Blog blog) {
        return adminMapper.updateBlog(blog);
    }

    @Override
    public Integer isBlogExist(Integer blogId) {
        return adminMapper.isBlogExist(blogId);
    }

    @Override
    public List<Comment> queryAllComment() {
        List<Comment> comments = adminMapper.queryAllComment();
        for (Comment comment : comments) {
            if (comment.getComment().length() > 22) {
                comment.setComment(comment.getComment().substring(0, 22) + "...");
            }
        }
        return comments;
    }

    @Override
    public Integer deleteCommentById(Integer comId) {
        return adminMapper.deleteCommentById(comId);
    }

    @Override
    public Integer queryCommentsNum() {
        return adminMapper.queryCommentsNum();
    }

    @Override
    public List<IpLog> queryLogsNum() {
        return adminMapper.queryLogsNum();
    }

    @Override
    public Integer deleteLogById(Integer uId) {
        return adminMapper.deleteLogById(uId);
    }

    @Override
    public List<IpLog> queryYesterdayAccessLogs() {
        return adminMapper.queryYesterdayAccessLogs();
    }

    @Override
    public Integer queryYesterdayAccessNum() {
        return adminMapper.queryYesterdayAccessNum();
    }

    @Override
    public Integer queryYesterdayAllAccessNum() {
        return adminMapper.queryYesterdayAllAccessNum();
    }

    @Override
    public List<String> queryYesterdayAccessAttribute() {
        return adminMapper.queryYesterdayAccessAttribute();
    }

    @Override
    public String adminIndex(String username, Model model) {
        long beginTime = System.currentTimeMillis();
        SystemUtils systemUtils = new SystemUtils();
        if (StringUtils.isNotBlank(username)) {
            model.addAttribute("userName", username);
            model.addAttribute("lastLoginTime", this.queryLoginTimeByName(username));
            model.addAttribute("loginNum", this.loginNum(username));
        } else {
            model.addAttribute("userName", BlogConstant.AdminConstant.DEFAULT_ADMIN_NAME);
            model.addAttribute("lastLoginTime", this.queryLoginTimeByName("lygoup@163.com"));
            model.addAttribute("loginNum", this.loginNum("lygoup@163.com"));
        }
        model.addAttribute("systemInfo", systemUtils);
        model.addAttribute("accessNum", this.accessNum());
        model.addAttribute("dbVersion", dbVersion());
        model.addAttribute("ipAddress", IpUtils.getIpAddress());
        model.addAttribute("blogsNum", this.blogsNum());
        model.addAttribute("argsNum", this.queryArgsNum());
        model.addAttribute("adminNum", this.adminNum());
        model.addAttribute("time", new Date());
        model.addAttribute("commentsNum", this.queryCommentsNum());
        long endTime = System.currentTimeMillis();
        model.addAttribute("useTime", endTime - beginTime);
        return "admin/index";
    }

    @Override
    public String loginControl(String userName, String userPwd, HttpSession session, Model model, HttpServletRequest request) {
        String redisKey = CacheConstant.LoginIpTimes.PREFIX + RequestUtils.getIpAddr();
        // 登录失败次数检测
        if (redisTemplate != null) {
            Integer curIpLoginTimes;
            if ((curIpLoginTimes = (Integer) redisTemplate.opsForValue().get(redisKey)) != null) {
                if (curIpLoginTimes >= BlogConstant.LoginConstant.MAX_LOGIN_TIME) {
                    if (curIpLoginTimes == BlogConstant.LoginConstant.MAX_LOGIN_TIME) {
                        mailUtils.sendMail("后台登录预警", "您的后台尝试登录失败次数已到达阈值，请注意：</br>尝试IP："
                                + RequestUtils.getIpAddr() + "</br>失败次数：" + curIpLoginTimes, MailConstant.MAIL_RECEIVE_ADDRESS);
                    }
                    redisTemplate.opsForValue().increment(redisKey);
                    log.warn("登录尝试过于频繁，拒绝登录:{}", redisKey);
                    model.addAttribute("msg", "您登录过于频繁，请稍后再试~");
                    return "admin/login";
                }
            } else {
                redisTemplate.opsForValue().set(redisKey, 0, CacheConstant.LoginIpTimes.TTL, TimeUnit.MINUTES);
            }
        }
        if (this.isLoginSuccess(userName, userPwd) == 1) {
            loginUserCheck.adminLoginAttributionCheck();
            this.addLoginNum(userName);
            this.updateLoginTime(userName, new Date());
            // 如果登陆成功就给用户一个session,key为登录用户名
            log.info("账户密码验证通过");
            session.setAttribute("loginUser", userName);
            return "redirect:/admin/adminIndex?username=" + userName;
        } else {
            if (redisTemplate != null) {
                redisTemplate.opsForValue().increment(redisKey);
                redisTemplate.expire(redisKey, CacheConstant.LoginIpTimes.TTL, TimeUnit.MINUTES);
            }
            log.warn("账户密码验证失败,尝试用户名:{},尝试密码:{}", userName, userPwd);
            model.addAttribute("msg", "用户名或密码错误！");
            return "admin/login";
        }
    }

    @Override
    public String CommentList(Model model) {
        ArrayList<CommentVo> commentVos = new ArrayList<>();
        List<Comment> comments = this.queryAllComment();
        List<BlogVo> blogVos = blogService.queryAllBlogMiNi();
        HashMap<String, BlogVo> blogId4Vos = new HashMap<>();
        for (BlogVo blog : blogVos) {
            blogId4Vos.put(blog.getBlogId(), blog);
        }
        if (!CollectionUtils.isEmpty(comments)) {
            for (Comment comment : comments) {
                CommentVo currentVo = new CommentVo();
                if (comment.getBlogId() != null) {
                    BeanUtils.copyProperties(comment, currentVo);
                    if (blogId4Vos.get(String.valueOf(comment.getBlogId())) != null) {
                        currentVo.setBlogTitle(blogId4Vos.get(String.valueOf(comment.getBlogId())).getTitle());
                    } else {
                        currentVo.setBlogTitle("非公开文章");
                    }
                }
                commentVos.add(currentVo);
            }
        }
        model.addAttribute("comments", commentVos);
        return "admin/comment";
    }

    @Override
    public String ArticleList(Model model) {
        model.addAttribute("blogs", this.queryAllBlogs());
        return "admin/article";
    }

    @Override
    public String searchArticleByKey(String key, Model model) {
        if (StringUtils.isNotBlank(key) && key.length() < 50) {
            List<Blog> blogs = adminMapper.searchArticleByKey(key);
            model.addAttribute("blogs", blogs);
            return "admin/search-article";
        } else {
            return "redirect:/error/404";
        }
    }

    @Override
    @SuppressWarnings("all")
    public String searchCommentByKey(String key, Model model) {
        ArrayList<CommentVo> commentVos = new ArrayList<>();
        if (StringUtils.isNotBlank(key) && key.length() < 50) {
            List<Comment> comments = adminMapper.searchCommentByKey(key);
            if (!CollectionUtils.isEmpty(comments)) {
                for (Comment comment : comments) {
                    CommentVo currentVo = new CommentVo();
                    if (comment.getBlogId() != null) {
                        BeanUtils.copyProperties(comment, currentVo);
                        currentVo.setBlogTitle(adminMapper.queryTitleById(comment.getBlogId()));
                    }
                    commentVos.add(currentVo);
                }
            }
            model.addAttribute("comments", commentVos);
            return "admin/search-comment";
        } else {
            return "redirect:/error/404";
        }
    }

    @Override
    public String searchAccessLogByKey(String key, Model model) {
        if (StringUtils.isNotBlank(key) && key.length() < 50) {
            List<IpLog> ipLogs = adminMapper.searchAccessLogByKey(key);
            if (!CollectionUtils.isEmpty(ipLogs)) {
                model.addAttribute("ipLogs", ipLogs);
            }
            return "admin/search-accessLog";
        } else {
            return "redirect:/error/404";
        }
    }

    /**
     * 每天早晨8点定时将昨天的博客访问数据，博客运行日志发送到管理员邮箱
     * 由于这里邮件复杂度较高，不使用邮件工具类发送
     */
    // @Scheduled(cron = "*/30 * * * * ?")  //30s发送一次，测试用
    @Scheduled(cron = "0 0 8 * * ?")
    public void sendMailToAdmin() throws MessagingException {
        StringBuilder mailText = new StringBuilder("");
        System.out.println(new Date() + "准备发送运行情况至管理员邮箱......");
        //创建一个复杂的邮件
        MimeMessage mimeMessage = javaMailSender.createMimeMessage();
        //用MimeMessageHelper帮助类来组装得到可编辑的邮件对象
        MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true);
        mimeMessageHelper.setSubject("早上好，昨天的博客统计数据已生成");
        List<IpLog> ipLogs = queryYesterdayAccessLogs();
        Integer yesterdayAccessNum = adminMapper.queryYesterdayAccessNum();
        Integer yesterdayAllAccessNum = adminMapper.queryYesterdayAllAccessNum();
        List<String> attributes = adminMapper.queryYesterdayAccessAttribute();
        mailText.append("&emsp;&emsp;昨天博客共有")
                .append(yesterdayAccessNum).append("人访问,")
                .append("发起了")
                .append(yesterdayAllAccessNum)
                .append("次访问请求,").append("他们是来自")
                .append(attributes.toString())
                .append("的访客。<br>")
                .append("&emsp;&emsp;--lygoup@163.com")
                .append("<hr style=\"border:2px double #e8e8e8\">");
        //编辑好发送的信息
        for (IpLog ipLog : ipLogs) {
            mailText.append("<p style=\"font-size: 15px;\">访问时间：<span style=\"color: cadetblue;\">")
                    .append(TimeFormatUtils.TimeParse(ipLog.getAccessDate()))
                    .append("</span><br>访问IP：<span style=\"color: cadetblue;\">")
                    .append(ipLog.getAccessIp())
                    .append("</span><br>访问地点：<span style=\"color: cadetblue;\">")
                    .append(ipLog.getIpAttribution())
                    .append("</span><br>路径：<a style=\"color: tomato;\">")
                    .append(ipLog.getAccessPath())
                    .append("</a><br><br></p>");
        }
        // 编辑邮件文本信息。true意思为：开启HTML支持
        mimeMessageHelper.setText(mailText.toString(), true);
        // 添加附件,linux服务器日志路径，本地测试请注释此语句
        // mimeMessageHelper.addAttachment(LogLocationConstant.MAIL_LOG_FILE_NAME, new File(LogLocationConstant.LOG_LOCATION));
        String mailTo = MailConstant.MAIL_RECEIVE_ADDRESS;
        mimeMessageHelper.setTo(mailTo);
        mimeMessageHelper.setFrom(MailConstant.MAIL_SEND_ACCOUNT_ADDRESS);
        // 发送邮件
        try {
            javaMailSender.send(mimeMessage);
        } catch (Exception e) {
            log.error("邮件发送服务端处理异常");
            e.printStackTrace();
        }
        log.info("运行情况已发送至邮箱:{}", mailTo);
        mailText = null;
    }


}
